using System;
using System.Collections.Generic;
using UnityEngine;

public class PhysicsBall : MonoBehaviour
{
	public delegate void OnCollisionDelegate(PhysicsBall ball, Vector3 hitPoint, Vector3 hitVel, HitObjectTag hitObject);

	public delegate void OnMissedServe();

	public OnCollisionDelegate onCollision;

	public HitParameters hitParams;

	public TableTennisMatchController match;

	public List<AudioClip> ballHitTableSounds = new List<AudioClip>();

	public List<AudioClip> ballHitPaddleSounds = new List<AudioClip>();

	public List<AudioClip> ballHitFloorSounds = new List<AudioClip>();

	public List<AudioClip> shoeSqueekSounds = new List<AudioClip>();

	public float pitch = 1f;

	public OnMissedServe onMissedServe;

	public FlightSequence flightSequence = new FlightSequence();

	public bool isServeBall;

	public TableTennisPlayerBase playerThatHitBall;

	public PhysicMaterial tablePhysicMaterial;

	private bool wasFlightActiveAtLastUpdate;

	private Vector3 lastVelocity = Vector3.zero;

	private Vector3 lastAngularVelocity = Vector3.zero;

	public float baseTimeScale = 1f;

	public float racketTimeScale = 1f;

	private Rigidbody _cachedRigidbody;

	public Vector3 prevPosition;

	private bool startedServe;

	private Vector3 oldGravity = Physics.gravity;

	public float visualSpinMultiplier = 1f;

	public float visualSpinMinimum = 10f;

	private long updateCount = 1L;

	public float maxServeSpin = 10f;

	private float remoteFrameTimescale = 1f;

	public AudioSource audioSource
	{
		get
		{
			return GetComponent<AudioSource>();
		}
	}

	public AudioClip shoeSqueek
	{
		get
		{
			List<AudioClip> list = shoeSqueekSounds;
			return list[UnityEngine.Random.Range(0, list.Count) % list.Count];
		}
	}

	public AudioClip ballHitFloor
	{
		get
		{
			List<AudioClip> list = ballHitFloorSounds;
			return list[UnityEngine.Random.Range(0, list.Count - 1)];
		}
	}

	public AudioClip ballHitTable
	{
		get
		{
			return ballHitTableSounds[UnityEngine.Random.Range(0, ballHitTableSounds.Count - 1)];
		}
	}

	public AudioClip paddleHitBall
	{
		get
		{
			return ballHitPaddleSounds[UnityEngine.Random.Range(0, ballHitPaddleSounds.Count - 1)];
		}
	}

	public bool isBallInGame { get; set; }

	public float timer { get; private set; }

	public PhysicMaterial myMaterial
	{
		get
		{
			return cachedRigidbody.GetComponent<Collider>().sharedMaterial;
		}
	}

	public float totalTimeScale
	{
		get
		{
			return baseTimeScale * racketTimeScale;
		}
	}

	public float framerateScale { get; protected set; }

	public Rigidbody cachedRigidbody
	{
		get
		{
			if (_cachedRigidbody == null)
			{
				_cachedRigidbody = GetComponent<Rigidbody>();
			}
			return _cachedRigidbody;
		}
	}

	public Vector3 position
	{
		get
		{
			return base.transform.position;
		}
		set
		{
			base.transform.position = value;
			prevPosition = position;
		}
	}

	public Vector3 localPosition
	{
		get
		{
			return base.transform.localPosition;
		}
		set
		{
			base.transform.localPosition = value;
			prevPosition = base.transform.position;
		}
	}

	public void PlaySound(AudioClip clip, float volume = 1f)
	{
		audioSource.pitch = pitch;
		audioSource.PlayOneShot(clip, volume);
	}

	public void ResetTimescale()
	{
		GGGameTime.gameTimescale = 1f;
		baseTimeScale = 1f;
		racketTimeScale = 1f;
	}

	public void ParentToObject(Transform parent)
	{
		SetKinematic(true);
		flightSequence.ClearStages();
		base.transform.parent = parent;
	}

	public void SetKinematic(bool isKinematic)
	{
		cachedRigidbody.isKinematic = isKinematic;
		cachedRigidbody.useGravity = !isKinematic;
		wasFlightActiveAtLastUpdate = false;
		if (isKinematic)
		{
			cachedRigidbody.velocity = Vector3.zero;
		}
	}

	public void Awake()
	{
		SetKinematic(true);
		FlightSequence obj = flightSequence;
		obj.onCollision = (FlightSequence.OnCollisionDelegate)Delegate.Remove(obj.onCollision, new FlightSequence.OnCollisionDelegate(ReportCollision));
		FlightSequence obj2 = flightSequence;
		obj2.onCollision = (FlightSequence.OnCollisionDelegate)Delegate.Combine(obj2.onCollision, new FlightSequence.OnCollisionDelegate(ReportCollision));
		ResetTimescale();
		framerateScale = 1f;
		updateCount = 1L;
	}

	public void Update()
	{
		Vector3 vector = position;
		vector.y = Mathf.Max(flightSequence.radius, vector.y);
		position = vector;
		float num = Mathf.Min(GGGameTime.deltaTime, Time.timeScale * GGGameTime.gameTimescale / 24f) * totalTimeScale;
		float num2 = num / (GGGameTime.deltaTime * totalTimeScale);
		if (!float.IsNaN(num2))
		{
			framerateScale = (framerateScale * (float)updateCount + num2) / (float)(updateCount + 1);
		}
		updateCount++;
		if (startedServe)
		{
			num = GGGameTime.deltaTime * remoteFrameTimescale;
			timer += num;
			if (base.transform.position.y < Table.tabletopy * 0.5f)
			{
				GGDebug.DebugLog("missed serve");
				startedServe = false;
				if (onMissedServe != null)
				{
					onMissedServe();
				}
			}
		}
		flightSequence.Update(num);
		if (flightSequence.isActive)
		{
			base.transform.position = flightSequence.Position();
			Vector3 vector2 = -Vector3.Cross(flightSequence.Velocity().normalized, Vector3.up);
			Vector3 eulerAngles = flightSequence.AngularVelocity() * Time.deltaTime * 360f * visualSpinMultiplier + visualSpinMinimum * vector2;
			base.transform.Rotate(eulerAngles, Space.World);
			lastVelocity = flightSequence.Velocity();
			lastAngularVelocity = flightSequence.AngularVelocity();
			wasFlightActiveAtLastUpdate = true;
		}
		else if (wasFlightActiveAtLastUpdate)
		{
			wasFlightActiveAtLastUpdate = false;
			Vector3 vector3 = base.transform.position;
			vector3.y = Mathf.Max(flightSequence.radius, vector3.y);
			base.transform.position = vector3;
			SetKinematic(false);
			cachedRigidbody.velocity = lastVelocity;
			Vector3 vector4 = -Vector3.Cross(flightSequence.Velocity().normalized, Vector3.up);
			Vector3 angularVelocity = lastAngularVelocity * 2f * (float)Math.PI * visualSpinMultiplier + visualSpinMinimum * vector4;
			cachedRigidbody.angularVelocity = angularVelocity;
			lastVelocity = Vector3.zero;
			lastAngularVelocity = Vector3.zero;
		}
	}

	public void ReportCollision(Vector3 position, Vector3 velocity, HitObjectTag hitObject)
	{
		if (hitObject == HitObjectTag.Table)
		{
			PlaySound(ballHitTable);
		}
		else
		{
			PlaySound(ballHitFloor);
		}
		if (!startedServe && onCollision != null)
		{
			onCollision(this, position, velocity, hitObject);
		}
	}

	public void StartServe(float velY)
	{
		SetKinematic(false);
		cachedRigidbody.velocity = new Vector3(0f, velY, 0f);
		timer = 0f;
		startedServe = true;
	}

	public void StartServe(Vector3 velocity, float remoteFrameTimescale = -1f)
	{
		Debug.Log("start serve velocity: " + velocity);
		this.remoteFrameTimescale = ((!(remoteFrameTimescale > 0f)) ? framerateScale : remoteFrameTimescale);
		FlightPool instance = SingletonInit<FlightPool>.instance;
		flightSequence.ClearStages();
		BallFlight flightFromPool = instance.GetFlightFromPool(typeof(GravityBallFlight));
		flightFromPool.gravity = Physics.gravity;
		Vector3 vector = UnityEngine.Random.insideUnitSphere * maxServeSpin;
		GGDebug.DebugLog(vector);
		FlightParams flightParams = default(FlightParams);
		flightParams.position = base.transform.position;
		flightParams.velocity = velocity;
		flightParams.angularVelocity = vector;
		FlightParams flightParams2 = flightParams;
		flightFromPool.SetInitialConditions(flightParams2, null);
		flightFromPool.Reset();
		MathEx.NullPoints nullPoints = MathEx.FindParabolaNullPoints((0f - Physics.gravity.y) / 2f, 0f - velocity.y, 0f - position.y);
		float max = nullPoints.Max;
		GGDebug.DebugLog("serve flight duration: " + max);
		FlightSequence.FlightPlanStage flightPlanStage = flightSequence.AddStage(flightFromPool, flightParams2, max, false, HitObjectTag.None);
		flightSequence.timeToLand = max;
		Vector3 landingPos = position;
		landingPos.y = 0f;
		flightSequence.landingPos = landingPos;
		flightSequence.Reset();
		timer = 0f;
		wasFlightActiveAtLastUpdate = false;
		startedServe = true;
	}

	public void HitOnServe()
	{
		SetKinematic(true);
		cachedRigidbody.velocity = Vector3.zero;
		startedServe = false;
	}
}
